unit uDZXML;

interface

uses
  SysUtils, Classes, uDZSysUtils, uDZStr;

function GetXMLNodeAttribute(Str: PWideChar; Len: Integer;
  var Value: WideString): Boolean;

function GetXMLNodeInnerXML(Str: PWideChar; Len: Integer;
  var Value: WideString): Boolean;

function XMLGetAttribute(const XML, Name: WideString;
  var Value: WideString): Boolean;

function XMLGetInnerXMLA(const XML, NodeName: AnsiString;
  var Value: AnsiString): Boolean;
  
function XMLGetInnerXMLW(const XML, NodeName: WideString;
  var Value: WideString): Boolean;

type
  TXMLNodeInfoA = object
  public
    NameLT: PAnsiChar;
    NameEnd: PAnsiChar;
    NameGT: PAnsiChar;
    function Name: AnsiString;
  end;
  PXMLNodeInfoA = TXMLNodeInfoA;

  TXMLNodeInfoW = object
    NameLT: PWideChar;
    NameEnd: PWideChar;
    NameGT: PWideChar;
    function Name: WideString;
  end;
  PXMLNodeInfoW = TXMLNodeInfoW;

  TDzXMLNode = class
  private
    fName: WideString;
    procedure SetName(const Value: WideString);
  public
    property Name: WideString read fName write SetName; 
  end;

  TDzXMLDocument = class
  private
    fCodePage: Integer;
    function GetEncoding: AnsiString;
    procedure SetEncoding(const Value: AnsiString);
  public
    procedure LoadFromStream(stream: TStream);
    procedure LoadFromFile(const FileName: string);
    property Encoding: AnsiString read GetEncoding write SetEncoding;
  end;
  
function XMLGetNodeInfoA(XML: PAnsiChar; XMLLen: Integer; Path: PAnsiChar;
  PathLen: Integer; out Info: TXMLNodeInfoA): Boolean;

function XMLGetNodeInfoW(XML: PWideChar; XMLLen: Integer; Path: PWideChar;
  PathLen: Integer; out Info: TXMLNodeInfoW): Boolean;

function XMLGetAttributeA(XML, XMLEnd, NamePtr, NameEnd: PAnsiChar;
  out ValuePtr, ValueEnd: PAnsiChar): Boolean;

function XMLGetAttributeW(XML, XMLEnd, NamePtr, NameEnd: PWideChar;
  out ValuePtr, ValueEnd: PWideChar): Boolean;

implementation

const
  XMLNodeNameEndChars = [#32, #9, #13, #10, '/', '>'];

function ValidateNodeNameA(Name1Ptr, Name1End, Name2Ptr, Name2End: PAnsiChar): Boolean;
begin
  if Name1Ptr = Name1End  then
  begin
    Result := True;
    Exit;
  end;

  Result := False;

  if Name1End - Name1Ptr <> Name2End - Name2Ptr then Exit;

  while Name1Ptr < Name1End do
  begin
    if Name1Ptr^ <> Name2Ptr^ then Exit;
    Inc(Name1Ptr); Inc(Name2Ptr);
  end;
  
  Result := True;
end;

function PathLevelA(Path, PathEnd: PAnsiChar; out NamePtr: PAnsiChar;
  out Index: Integer): PAnsiChar;
begin
  Index := 1; NamePtr := Path;
  while (NamePtr < PathEnd) and not (NamePtr^ in ['/', '[']) do Inc(NamePtr);
  Result := NamePtr;
  if (NamePtr = PathEnd) or (NamePtr^ = '/') then Exit;

  //[\d+]
  Index := 0;
  Inc(Result);
  while Result < PathEnd do
  begin
    case Result^ of
      '0'..'9':
        begin
          Index := Index * 10 + PByte(Result)^ and $0f;
          Inc(Result);
        end;
      ']':
        begin
          Inc(Result);
          Exit;
        end;
      else Exit;
    end;
  end;
end;

function SkipXMLNodeA(Node, XMLEnd: PAnsiChar; out NodeEnd: PAnsiChar): Boolean;
var
  Ptr, NodeNamePtr, NodeNameEnd: PAnsiChar;
  Depth: Integer;
begin
  Result := False;
  Depth := 0;
  NodeNamePtr := nil;
  NodeNameEnd := nil;
  NodeEnd := Node;
  while True do
  begin
    while (NodeEnd < XMLEnd) and (NodeEnd^ <> '<') do Inc(NodeEnd);
    if NodeEnd = XMLEnd then Exit;
    Ptr := NodeEnd;
    while (NodeEnd < XMLEnd) and not (NodeEnd^ in XMLNodeNameEndChars) do Inc(NodeEnd);
    if NodeEnd >= XMLEnd then Exit;
    if NodeEnd^ = '/' then
{.$region 'NodeEnd^ = ''/'''}
    begin
      if NodeNamePtr = nil then
      begin
        NodeNamePtr := Ptr + 1;
        NodeNameEnd := NodeEnd;
        if NodeNamePtr = NodeNameEnd then Exit;
        Inc(NodeEnd);
        Result := (NodeEnd < XMLEnd) and (NodeEnd^ = '>');
        Exit;
      end
      else begin
        if NodeEnd = Ptr + 1 then
        begin
          // </...
          while (NodeEnd < XMLEnd) and (NodeEnd^ <> '>') do Inc(NodeEnd);
          if NodeEnd = XMLEnd then Exit;
          if Depth = 0 then
          begin
            Result := ValidateNodeNameA(NodeNamePtr, NodeNameEnd, Ptr + 2, NodeEnd);
            Exit;
          end
          else Dec(Depth);
        end
        else begin
          Inc(NodeEnd);
          if (NodeEnd = XMLEnd) or (NodeEnd^ <> '>') then Exit;
        end;
      end;
    end
    {.$endregion}
    else if NodeEnd^ = '>' then
    {.$region 'NodeEnd^ = ''>'''}
    begin
      if NodeEnd = Ptr + 1 then Exit;
      if NodeNamePtr = nil then
      begin
        NodeNamePtr := Ptr + 1;
        NodeNameEnd := NodeEnd;
        Inc(NodeEnd);
      end
      else Inc(Depth);
    end
    {.$endregion}
    else begin
      if NodeEnd = Ptr + 1 then Exit;
      if NodeNamePtr = nil then
      begin
        NodeNamePtr := Ptr + 1;
        NodeNameEnd := NodeEnd;
        Inc(NodeEnd);
      end
      else Inc(Depth);
      while (NodeEnd < XMLEnd) and not (NodeEnd^ in ['/', '>']) do Inc(NodeEnd);
      if NodeEnd = XMLEnd then Exit;
      if NodeEnd^ = '/' then
      begin
        Inc(NodeEnd);
        if (NodeEnd = XMLEnd) or (NodeEnd^ <> '>') then Exit;
        if Depth = 0 then begin Result := True; Exit; end;
        Dec(Depth);
      end;
      Inc(NodeEnd);
    end;
  end;
end;

function XMLGetNodeInfoA(XML: PAnsiChar; XMLLen: Integer; Path: PAnsiChar;
  PathLen: Integer; out Info: TXMLNodeInfoA): Boolean;
var
  NodeName, NodeNameEnd, PathPtr, PathEnd, XmlEnd: PAnsiChar;
  Index: Integer;
  ChildCnt: Integer;
begin
  Result := False;
  PathEnd := Path + PathLen;
  XmlEnd := XML + XMLLen;
  Info.NameGT := XML;
  while (Path < PathEnd) and (Path^ = '/') do
  begin
    NodeName := Path + 1;
    PathPtr := PathLevelA(NodeName, PathEnd, NodeNameEnd, Index);
    if (PathPtr < PathEnd) and (PathPtr^ <> '/') then Exit;
    ChildCnt := 0;

    while True do
    begin
      while (Info.NameGT < XMLEnd) and (Info.NameGT^ <> '<') do Inc(Info.NameGT);
      if Info.NameGT = XmlEnd then Exit;
      Info.NameLT := Info.NameGT;
      Info.NameEnd := AnsiSeekChar(Info.NameLT, XMLEnd, XMLNodeNameEndChars);
      if (Info.NameEnd = XmlEnd) or (Info.NameEnd = Info.NameLT + 1) then Exit;
      if ValidateNodeNameA(NodeName, NodeNameEnd, Info.NameLT + 1, Info.NameEnd) then
      begin
        Inc(ChildCnt);
        if ChildCnt = Index then
        begin
          Path := PathPtr;
          Info.NameGT := Info.NameEnd;
          while (Info.NameGT < XmlEnd) and not (Info.NameGT^ in ['/', '>']) do
            Inc(Info.NameGT);
          if Info.NameGT = XmlEnd then Exit;
          if Info.NameGT^ = '/' then
          begin
            // <.../>
            Inc(Info.NameGT);
            if (Info.NameGT = XmlEnd) or (Info.NameGT^ <> '>') then Exit;
            if (PathPtr = PathEnd) or (PathPtr + 1 = PathEnd) then Result := True;
            Exit;
          end;
          Break;
        end
        else if not SkipXMLNodeA(Info.NameLT, XmlEnd, Info.NameGT) then
          Exit;
      end
      else if not SkipXMLNodeA(Info.NameLT, XmlEnd, Info.NameGT) then
        Exit;
    end;
  end;

  Result := True;
end;

function ValidateNodeNameW(Name1Ptr, Name1End, Name2Ptr, Name2End: PWideChar): Boolean;
begin
  if Name1Ptr = Name1End  then
  begin
    Result := True;
    Exit;
  end;

  Result := False;

  if Name1End - Name1Ptr <> Name2End - Name2Ptr then Exit;

  while Name1Ptr < Name1End do
  begin
    if Name1Ptr^ <> Name2Ptr^ then Exit;
    Inc(Name1Ptr); Inc(Name2Ptr);
  end;
  
  Result := True;
end;

function PathLevelW(Path, PathEnd: PWideChar; out NamePtr: PWideChar;
  out Index: Integer): PWideChar;
begin
  Index := 1; NamePtr := Path;
  while (NamePtr < PathEnd) and (NamePtr^ <> '/') and (NamePtr^ <> '[') do Inc(NamePtr);
  Result := NamePtr;
  if (NamePtr = PathEnd) or (NamePtr^ = '/') then Exit;

  //[\d+]
  Index := 0;
  Inc(Result);
  while Result < PathEnd do
  begin
    case Result^ of
      '0'..'9':
        begin
          Index := Index * 10 + PByte(Result)^ and $0f;
          Inc(Result);
        end;
      ']':
        begin
          Inc(Result);
          Exit;
        end;
      else Exit;
    end;
  end;
end;

function SkipXMLNodeW(Node, XMLEnd: PWideChar; out NodeEnd: PWideChar): Boolean;
var
  Ptr, NodeNamePtr, NodeNameEnd: PWideChar;
  Depth: Integer;
begin
  Result := False;
  Depth := 0;
  NodeNamePtr := nil;
  NodeNameEnd := nil;
  NodeEnd := Node;
  while True do
  begin
    while (NodeEnd < XMLEnd) and (NodeEnd^ <> '<') do Inc(NodeEnd);
    if NodeEnd = XMLEnd then Exit;
    Ptr := NodeEnd;
    while (NodeEnd < XMLEnd) and ((PWord(NodeEnd)^ > $7f) or
      not (AnsiChar(NodeEnd^) in XMLNodeNameEndChars)) do Inc(NodeEnd);
    if NodeEnd >= XMLEnd then Exit;
    if NodeEnd^ = '/' then
{.$region 'NodeEnd^ = ''/'''}
    begin
      if NodeNamePtr = nil then
      begin
        NodeNamePtr := Ptr + 1;
        NodeNameEnd := NodeEnd;
        if NodeNamePtr = NodeNameEnd then Exit;
        Inc(NodeEnd);
        Result := (NodeEnd < XMLEnd) and (NodeEnd^ = '>');
        Exit;
      end
      else begin
        if NodeEnd = Ptr + 1 then
        begin
          // </...
          while (NodeEnd < XMLEnd) and (NodeEnd^ <> '>') do Inc(NodeEnd);
          if NodeEnd = XMLEnd then Exit;
          if Depth = 0 then
          begin
            Result := ValidateNodeNameW(NodeNamePtr, NodeNameEnd, Ptr + 2, NodeEnd);
            Exit;
          end
          else Dec(Depth);
        end
        else begin
          Inc(NodeEnd);
          if (NodeEnd = XMLEnd) or (NodeEnd^ <> '>') then Exit;
        end;
      end;
    end
    {.$endregion}
    else if NodeEnd^ = '>' then
    {.$region 'NodeEnd^ = ''>'''}
    begin
      if NodeEnd = Ptr + 1 then Exit;
      if NodeNamePtr = nil then
      begin
        NodeNamePtr := Ptr + 1;
        NodeNameEnd := NodeEnd;
        Inc(NodeEnd);
      end
      else Inc(Depth);
    end
    {.$endregion}
    else begin
      if NodeEnd = Ptr + 1 then Exit;
      if NodeNamePtr = nil then
      begin
        NodeNamePtr := Ptr + 1;
        NodeNameEnd := NodeEnd;
        Inc(NodeEnd);
      end
      else Inc(Depth);
      while (NodeEnd < XMLEnd) and (NodeEnd^ <> '/') and (NodeEnd^ <> '>') do Inc(NodeEnd);
      if NodeEnd = XMLEnd then Exit;
      if NodeEnd^ = '/' then
      begin
        Inc(NodeEnd);
        if (NodeEnd = XMLEnd) or (NodeEnd^ <> '>') then Exit;
        if Depth = 0 then begin Result := True; Exit; end;
        Dec(Depth);
      end;
      Inc(NodeEnd);
    end;
  end;
end;

function XMLGetNodeInfoW(XML: PWideChar; XMLLen: Integer; Path: PWideChar;
  PathLen: Integer; out Info: TXMLNodeInfoW): Boolean;
var
  NodeName, NodeNameEnd, PathPtr, PathEnd, XmlEnd: PWideChar;
  Index: Integer;
  ChildCnt: Integer;
begin
  Result := False;
  PathEnd := Path + PathLen;
  XmlEnd := XML + XMLLen;
  Info.NameGT := XML;
  while (Path < PathEnd) and (Path^ = '/') do
  begin
    NodeName := Path + 1;
    PathPtr := PathLevelW(NodeName, PathEnd, NodeNameEnd, Index);
    if (PathPtr < PathEnd) and (PathPtr^ <> '/') then Exit;
    ChildCnt := 0;

    while True do
    begin
      while (Info.NameGT < XMLEnd) and (Info.NameGT^ <> '<') do Inc(Info.NameGT);
      if Info.NameGT = XmlEnd then Exit;
      Info.NameLT := Info.NameGT;
      Info.NameEnd := Info.NameLT;
      while (Info.NameEnd < XMLEnd) and ((PWord(Info.NameEnd)^ > $7f) or
         not (AnsiChar(Info.NameEnd^) in XMLNodeNameEndChars)) do
        Inc(Info.NameEnd);
      if (Info.NameEnd = XmlEnd) or (Info.NameEnd = Info.NameLT + 1) then Exit;
      if ValidateNodeNameW(NodeName, NodeNameEnd, Info.NameLT + 1, Info.NameEnd) then
      begin
        Inc(ChildCnt);
        if ChildCnt = Index then
        begin
          Path := PathPtr;
          Info.NameGT := Info.NameEnd;
          while (Info.NameGT < XmlEnd) and (Info.NameGT^ <> '/') and
            (Info.NameGT^ <> '>') do Inc(Info.NameGT);
          if Info.NameGT = XmlEnd then Exit;
          if Info.NameGT^ = '/' then
          begin
            // <.../>
            Inc(Info.NameGT);
            if (Info.NameGT = XmlEnd) or (Info.NameGT^ <> '>') then Exit;
            if (PathPtr = PathEnd) or (PathPtr + 1 = PathEnd) then Result := True;
            Exit;
          end;
          Break;
        end
        else if not SkipXMLNodeW(Info.NameLT, XmlEnd, Info.NameGT) then
          Exit;
      end
      else if not SkipXMLNodeW(Info.NameLT, XmlEnd, Info.NameGT) then
        Exit;
    end;
  end;

  Result := True;
end;

function XMLReadAttrValueA(XML, XMLEnd: PAnsiChar;
  out ValuePtr, ValueEnd: PAnsiChar): Boolean;
begin
  Result := False;
  ValuePtr := XML;
  while (ValuePtr < XMLEnd) and (ValuePtr^ <> '=') do Inc(ValuePtr);
  if ValuePtr = XMLEnd then Exit;
  Inc(ValuePtr);
  while (ValuePtr < XMLEnd) and (ValuePtr^ in [#9, #10, #13, #32]) do Inc(ValuePtr);
  if (ValuePtr = XMLEnd) or ((ValuePtr^ <> #39) and (ValuePtr^ <> #34)) then Exit;
  ValueEnd := ValuePtr + 1;
  while (ValueEnd < XMLEnd) and (ValueEnd^ <> ValuePtr^) do Inc(ValueEnd);
  if ValueEnd = XMLEnd then Exit;
  Inc(ValuePtr);
  Result := True;
end;

function XMLGetAttributeA(XML, XMLEnd, NamePtr, NameEnd: PAnsiChar;
  out ValuePtr, ValueEnd: PAnsiChar): Boolean;
var
  nptr, nend: PAnsiChar;
begin
  Result := False;
  nptr := XML;
  while True do
  begin
    while (nptr < XMLEnd) and (nptr^ in [#9, #10, #13, #32]) do Inc(nptr);
    if (nptr = XMLEnd) or (nptr^ in ['/', '>']) then Exit;
    nend := nptr + 1;
    while (nend < XMLEnd) and not (nend^ in [#9, #10, #13, #32, '=']) do Inc(nend);
    if nend = XMLEnd then Exit;
    if not XMLReadAttrValueA(nend, XMLEnd, ValuePtr, ValueEnd) then Exit;
    if ValidateNodeNameA(NamePtr, NameEnd, nptr, nend) then
    begin
      Result := True;
      Exit;
    end;
    nptr := ValueEnd + 1;
  end;
end;

function XMLReadAttrValueW(XML, XMLEnd: PWideChar;
  out ValuePtr, ValueEnd: PWideChar): Boolean;
begin
  Result := False;
  ValuePtr := XML;
  while (ValuePtr < XMLEnd) and (ValuePtr^ <> '=') do Inc(ValuePtr);
  if ValuePtr = XMLEnd then Exit;
  Inc(ValuePtr);
  while (ValuePtr < XMLEnd) and (PWord(ValuePtr)^ <= $7f) and
    (AnsiChar(ValuePtr^) in [#9, #10, #13, #32]) do Inc(ValuePtr);
  if (ValuePtr = XMLEnd) or ((ValuePtr^ <> #39) and (ValuePtr^ <> #34)) then Exit;
  ValueEnd := ValuePtr + 1;
  while (ValueEnd < XMLEnd) and (ValueEnd^ <> ValuePtr^) do Inc(ValueEnd);
  if ValueEnd = XMLEnd then Exit;
  Inc(ValuePtr);
  Result := True;
end;

function XMLGetAttributeW(XML, XMLEnd, NamePtr, NameEnd: PWideChar;
  out ValuePtr, ValueEnd: PWideChar): Boolean;
var
  nptr, nend: PWideChar;
begin
  Result := False;
  nptr := XML;
  while True do
  begin
    while (nptr < XMLEnd) and (PWord(nptr)^ < 127) and
      (AnsiChar(nptr^) in [#9, #10, #13, #32]) do Inc(nptr);
    if (nptr = XMLEnd) or (nptr^ = '/') or (nptr^ = '>') then Exit;
    nend := nptr + 1;
    while (nend < XMLEnd) and ((PWord(nend)^ > $7f) or not
      (AnsiChar(nend^) in [#9, #10, #13, #32, '='])) do Inc(nend);
    if nend = XMLEnd then Exit;
    if not XMLReadAttrValueW(nend, XMLEnd, ValuePtr, ValueEnd) then Exit;
    if ValidateNodeNameW(NamePtr, NameEnd, nptr, nend) then
    begin
      Result := True;
      Exit;
    end;
    nptr := ValueEnd + 1;
  end;
end;

function GetXMLNodeAttribute(Str: PWideChar; Len: Integer;
  var Value: WideString): Boolean;
var
  start: PWideChar;
begin
  Result := False;
  while ((Len > 0) and (Str^ <> '=')) do
  begin
    if Str^ <> #32 then Exit;
    Inc(Str);
    Dec(Len);
  end;
  Inc(Str);
  Dec(Len);
  if (Len <= 0) then Exit;
  while ((Len > 0) and (Str^ <> '"')) do
  begin
    if (Str^ <> #32) then Exit;
    Inc(Str);
    Dec(Len);
  end;
  Inc(Str);
  Dec(Len);
  if (Len <= 0) then Exit;
  start := Str;
  while ((Len > 0) and (Str^ <> '"')) do
  begin
    if (Str^ = '>') then Exit;
    Inc(Str);
    Dec(Len);
  end;
  if (Len = 0) then Exit;
  SetString(Value, start, Str - start);
  Result := True;
end;

function GetXMLNodeInnerXML(Str: PWideChar; Len: Integer;
  var Value: WideString): Boolean;
var
  start: PWideChar;
begin
  Result := False;
  while (Len > 0) and (Str^ <> '>') do
  begin
    if Str^ = '<' then Exit;
    Inc(Str);
    Dec(Len);
  end;
  Inc(Str);
  Dec(Len);
  if Len <= 0 then Exit;
  start := Str;
  while (Len > 0) and (Str^ <> '<') do
  begin
    if Str^ = '>' then Exit;
    Inc(Str);
    Dec(Len);
  end;
  if Len <= 0 then Exit;
  SetString(Value, start, Str - start);
  Result := True;
end;

function XMLGetAttribute(const XML, Name: WideString;
  var Value: WideString): Boolean;
var
  i: Integer;
  p: PWideChar;
begin
  Result := False;
  i := Pos(Name, XML);
  if i <= 0 then Exit;
  p := PWideChar(XML) + i - 1 + Length(Name);
  Result := GetXMLNodeAttribute(p, Length(XML) + 1 - i - Length(Name), Value);
end;

function XMLGetInnerXMLA(const XML, NodeName: AnsiString;
  var Value: AnsiString): Boolean;
var
  i, Len: Integer;
  InnerPtr, ChPtr: PAnsiChar;
begin
  Result := False;
  i := Pos('<' + NodeName + '>', XML);
  if i <= 0 then Exit;
  InnerPtr := PAnsiChar(XML) + i - 1 + Length(NodeName) + 2;
  ChPtr := InnerPtr;
  Len := Length(XML) + 1 - i - Length(NodeName) - 2;
  while (Len > 0) and (ChPtr^ <> '<') do
  begin
    Inc(ChPtr);
    Dec(Len);
  end;
  if Len > 0 then
  begin
    Value := AnsiStrFromBuf(InnerPtr, ChPtr - InnerPtr);
    Result := True;
  end;
end;

function XMLGetInnerXMLW(const XML, NodeName: WideString;
  var Value: WideString): Boolean;
var
  i, Len: Integer;
  InnerPtr, ChPtr: PWideChar;
begin
  Result := False;
  i := Pos('<' + NodeName + '>', XML);
  if i <= 0 then Exit;
  InnerPtr := PWideChar(XML) + i - 1 + Length(NodeName) + 2;
  ChPtr := InnerPtr;
  Len := Length(XML) + 1 - i - Length(NodeName) - 2;
  while (Len > 0) and (ChPtr^ <> '<') do
  begin
    Inc(ChPtr);
    Dec(Len);
  end;
  if Len > 0 then
  begin
    Value := WideStrFromBuf(InnerPtr, ChPtr - InnerPtr);
    Result := True;
  end;
end;

{ TXMLNodeInfoA }

function TXMLNodeInfoA.Name: AnsiString;
begin
  Result := AnsiStrFromBuf(NameLT + 1, NameEnd - NameLT - 1);
end;

{ TXMLNodeInfoW }

function TXMLNodeInfoW.Name: WideString;
begin
  Result := WideStrFromBuf(NameLT + 1, 2 * (NameEnd - NameLT - 1));  
end;

{ TDzXMLNode }

procedure TDzXMLNode.SetName(const Value: WideString);
begin
  fName := Value;
end;

{ TDzXMLDocument }

function TDzXMLDocument.GetEncoding: AnsiString;
begin
  Result := GetCodePageName(fCodePage);  
end;

procedure TDzXMLDocument.LoadFromFile(const FileName: string);
var
  stream: TFileStream;
begin
  stream := TFileStream.Create(FileName, fmOpenRead);
  try
    Self.LoadFromStream(stream);
  finally
    stream.Free;
  end;
end;

procedure TDzXMLDocument.LoadFromStream(stream: TStream);
var
  w: Word;
begin
  stream.ReadBuffer(w, SizeOf(Word));
  //
  
end;

procedure TDzXMLDocument.SetEncoding(const Value: AnsiString);
begin
  fCodePage := GetCodePageID(Value);
end;

end.
